summaryrefslogtreecommitdiffstats
path: root/src/tests/input_common/calibration_configuration_job.cpp
blob: 516ff1b3028745a1d8c32c02b178ff28dcbebeba (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <array>
#include <string>
#include <thread>
#include <boost/asio.hpp>
#include <boost/crc.hpp>
#include <catch2/catch_test_macros.hpp>

#include "input_common/drivers/udp_client.h"
#include "input_common/helpers/udp_protocol.h"

class FakeCemuhookServer {
public:
    FakeCemuhookServer()
        : socket(io_service, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0)) {}

    ~FakeCemuhookServer() {
        is_running = false;
        boost::system::error_code error_code;
        socket.shutdown(boost::asio::socket_base::shutdown_both, error_code);
        socket.close();
        if (handler.joinable()) {
            handler.join();
        }
    }

    u16 GetPort() {
        return socket.local_endpoint().port();
    }

    std::string GetHost() {
        return socket.local_endpoint().address().to_string();
    }

    void Run(const std::vector<InputCommon::CemuhookUDP::Response::TouchPad> touch_movement_path) {
        constexpr size_t HeaderSize = sizeof(InputCommon::CemuhookUDP::Header);
        constexpr size_t PadDataSize =
            sizeof(InputCommon::CemuhookUDP::Message<InputCommon::CemuhookUDP::Response::PadData>);

        REQUIRE(touch_movement_path.size() > 0);
        is_running = true;
        handler = std::thread([touch_movement_path, this]() {
            auto current_touch_position = touch_movement_path.begin();
            while (is_running) {
                boost::asio::ip::udp::endpoint sender_endpoint;
                boost::system::error_code error_code;
                auto received_size = socket.receive_from(boost::asio::buffer(receive_buffer),
                                                         sender_endpoint, 0, error_code);

                if (received_size < HeaderSize) {
                    continue;
                }

                InputCommon::CemuhookUDP::Header header{};
                std::memcpy(&header, receive_buffer.data(), HeaderSize);
                switch (header.type) {
                case InputCommon::CemuhookUDP::Type::PadData: {
                    InputCommon::CemuhookUDP::Response::PadData pad_data{};
                    pad_data.touch[0] = *current_touch_position;
                    const auto pad_message = InputCommon::CemuhookUDP::CreateMessage(
                        InputCommon::CemuhookUDP::SERVER_MAGIC, pad_data, 0);
                    std::memcpy(send_buffer.data(), &pad_message, PadDataSize);
                    socket.send_to(boost::asio::buffer(send_buffer, PadDataSize), sender_endpoint,
                                   0, error_code);

                    bool can_advance =
                        std::next(current_touch_position) != touch_movement_path.end();
                    if (can_advance) {
                        std::advance(current_touch_position, 1);
                    }
                    break;
                }
                case InputCommon::CemuhookUDP::Type::PortInfo:
                case InputCommon::CemuhookUDP::Type::Version:
                default:
                    break;
                }
            }
        });
    }

private:
    boost::asio::io_service io_service;
    boost::asio::ip::udp::socket socket;
    std::array<u8, InputCommon::CemuhookUDP::MAX_PACKET_SIZE> send_buffer;
    std::array<u8, InputCommon::CemuhookUDP::MAX_PACKET_SIZE> receive_buffer;
    bool is_running = false;
    std::thread handler;
};

TEST_CASE("CalibrationConfigurationJob completed", "[input_common]") {
    Common::Event complete_event;
    FakeCemuhookServer server;
    server.Run({{
                    .is_active = 1,
                    .x = 0,
                    .y = 0,
                },
                {
                    .is_active = 1,
                    .x = 200,
                    .y = 200,
                }});

    InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status status{};
    u16 min_x{};
    u16 min_y{};
    u16 max_x{};
    u16 max_y{};
    InputCommon::CemuhookUDP::CalibrationConfigurationJob job(
        server.GetHost(), server.GetPort(),
        [&status,
         &complete_event](InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status status_) {
            status = status_;
            if (status ==
                InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status::Completed) {
                complete_event.Set();
            }
        },
        [&](u16 min_x_, u16 min_y_, u16 max_x_, u16 max_y_) {
            min_x = min_x_;
            min_y = min_y_;
            max_x = max_x_;
            max_y = max_y_;
        });

    complete_event.WaitUntil(std::chrono::system_clock::now() + std::chrono::seconds(10));
    REQUIRE(status == InputCommon::CemuhookUDP::CalibrationConfigurationJob::Status::Completed);
    REQUIRE(min_x == 0);
    REQUIRE(min_y == 0);
    REQUIRE(max_x == 200);
    REQUIRE(max_y == 200);
}